﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Slab.Usb
{
    public class ReturnCode
    {
        public const int HID_DEVICE_SUCCESS = 0x00;
        public const int HID_DEVICE_NOT_FOUND = 0x01;
        public const int HID_DEVICE_NOT_OPENED = 0x02;
        public const int HID_DEVICE_ALREADY_OPENED = 0x03;
        public const int HID_DEVICE_TRANSFER_TIMEOUT = 0x04;
        public const int HID_DEVICE_TRANSFER_FAILED = 0x05;
        public const int HID_DEVICE_CANNOT_GET_HID_INFO = 0x06;
        public const int HID_DEVICE_HANDLE_ERROR = 0x07;
        public const int HID_DEVICE_INVALID_BUFFER_SIZE = 0x08;
        public const int HID_DEVICE_SYSTEM_CODE = 0x09;
        public const int HID_DEVICE_UNKNOWN_ERROR = 0xFF;
    }

    public class StringType
    {
        public const int HID_VID_STRING = 0x01;
        public const int HID_PID_STRING = 0x02;
        public const int HID_PATH_STRING = 0x03;
        public const int HID_SERIAL_STRING = 0x04;
    }

    public class HidDevice
    {
        #region DllImport

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperGetNumHidDevices(int vid, int pid);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern IntPtr CHIDDeviceWrapperCtor();

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern void CHIDDeviceWrapperDispose(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperOpen(IntPtr ptr, int deviceIndex, int vid, int pid, int numInputBuffers);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern bool CHIDDeviceWrapperIsOpened(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperClose(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperGetString(IntPtr ptr, int hidStringType,[MarshalAs(UnmanagedType.LPStr)] StringBuilder deviceString, int deviceStringLength);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperGetInputReport_Control(IntPtr ptr, byte[] buffer, int bufferSize);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperSetOutputReport_Control(IntPtr ptr, byte[] buffer, int bufferSize);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperGetInputReportBufferLength(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperGetOutputReportBufferLength(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperGetFeatureReportBufferLength(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern int CHIDDeviceWrapperGetMaxReportRequest(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern bool CHIDDeviceWrapperFlushBuffers(IntPtr ptr);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern void CHIDDeviceWrapperGetTimeouts(IntPtr ptr, ref uint getReportTimeout, ref uint setReportTimeout);

        [DllImport("SLABHIDDeviceWrapper_Cpp.dll")]
        private static extern void CHIDDeviceWrapperSetTimeouts(IntPtr ptr, uint getReportTimeout, uint setReportTimeout);

        #endregion

        private IntPtr ptr = IntPtr.Zero;
        private uint getReportTimeout;
        private uint setReportTimeout;

        public static int GeNumDevices(int vid, int pid)
        {
            return CHIDDeviceWrapperGetNumHidDevices(vid, pid);
        }

        public HidDevice()
        {
            ptr = CHIDDeviceWrapperCtor();
            CHIDDeviceWrapperGetTimeouts(ptr, ref getReportTimeout, ref setReportTimeout);
        }

        public void Open(int deviceIndex, int vid, int pid)
        {
            int n=CHIDDeviceWrapperOpen(ptr,deviceIndex,vid,pid,1024);
            if(n!=ReturnCode.HID_DEVICE_SUCCESS)
                throw new Exception("Error when open device, return code is:"+n.ToString());
        }

        public bool IsOpen
        {
            get
            {
                return CHIDDeviceWrapperIsOpened(ptr);
            }
        }

        public void Close()
        {
            if(IsOpen==false)
                return;

            int n=CHIDDeviceWrapperClose(ptr);
            
            if(n!=ReturnCode.HID_DEVICE_SUCCESS)
                throw new Exception("Error when close device, return code is:"+n.ToString());
        }

        public string VendorID
        {
            get
            {
                StringBuilder sb = new StringBuilder(1024);
                int n = CHIDDeviceWrapperGetString(ptr, StringType.HID_VID_STRING, sb, 1024);

                if (n != ReturnCode.HID_DEVICE_SUCCESS)
                    throw new Exception("Error when get string, return code is:" + n.ToString());

                return sb.ToString();
            }
        }

        public string ProductID
        {
            get
            {
                StringBuilder sb = new StringBuilder(1024);
                int n = CHIDDeviceWrapperGetString(ptr, StringType.HID_PID_STRING, sb, 1024);

                if (n != ReturnCode.HID_DEVICE_SUCCESS)
                    throw new Exception("Error when get string, return code is:" + n.ToString());

                return sb.ToString();
            }
        }

        public string Path
        {
            get
            {
                StringBuilder sb = new StringBuilder(1024);
                int n = CHIDDeviceWrapperGetString(ptr, StringType.HID_PATH_STRING, sb, 1024);

                if (n != ReturnCode.HID_DEVICE_SUCCESS)
                    throw new Exception("Error when get string, return code is:" + n.ToString());

                return sb.ToString();
            }
        }

        public string Serial
        {
            get
            {
                StringBuilder sb = new StringBuilder(1024);
                int n = CHIDDeviceWrapperGetString(ptr, StringType.HID_SERIAL_STRING, sb, 1024);

                if (n != ReturnCode.HID_DEVICE_SUCCESS)
                    throw new Exception("Error when get string, return code is:" + n.ToString());

                return sb.ToString();
            }
        }

        public void GetInputReport_Control(byte[] buffer, int size)
        {
            int n = CHIDDeviceWrapperGetInputReport_Control(ptr, buffer, size);
            if (n != ReturnCode.HID_DEVICE_SUCCESS)
                throw new Exception("Error when get get input report, return code is:" + n.ToString());
        }

        public void SetOutputReport_Control(byte[] buffer, int size)
        {
            int n = CHIDDeviceWrapperSetOutputReport_Control(ptr, buffer, size);
            if (n != ReturnCode.HID_DEVICE_SUCCESS)
                throw new Exception("Error when set output report, return code is:" + n.ToString());
        }

        public int InputReportBufferLength
        {
            get
            {
                return CHIDDeviceWrapperGetInputReportBufferLength(ptr);
            }
        }

        public int OutputReportBufferLength
        {
            get
            {
                return CHIDDeviceWrapperGetOutputReportBufferLength(ptr);
            }
        }

        public int FeatureReportBufferLength
        {
            get
            {
                return CHIDDeviceWrapperGetFeatureReportBufferLength(ptr);
            }
        }

        public int MaxReportRequest
        {
            get
            {
                return CHIDDeviceWrapperGetMaxReportRequest(ptr);
            }
        }

        public void FlushBuffers()
        {
            bool n = CHIDDeviceWrapperFlushBuffers(ptr);

            if (n != true)
                throw new Exception("Error when flush buffers");
        }

        public uint GetReportTimeout
        {
            get
            {
                return getReportTimeout;
            }
            set
            {
                getReportTimeout = value;
                CHIDDeviceWrapperSetTimeouts(ptr, getReportTimeout, setReportTimeout);
            }
        }

        public uint SetReportTimeout
        {
            get
            {
                return setReportTimeout;
            }
            set
            {
                setReportTimeout = value;
                CHIDDeviceWrapperSetTimeouts(ptr, getReportTimeout, setReportTimeout);
            }
        }
    }
}